<template>
  <div>
    <div style="text-align: right">
      <el-checkbox v-model="value.rawMode" label="raw" style="margin-right: 10px;" @change="handleRawCheckboxChange" />
      <el-button-group style="margin-top: 0px; margin-right: 12px; margin-bottom: 4px;">
        <el-tooltip :open-delay="500" content="历史记录" placement="top">
          <el-button :disabled="element.id===undefined" icon="el-icon-link" size="mini" type="info" @click="handleHistory" />
        </el-tooltip>
        <el-tooltip content="保存" placement="top" :open-delay="500">
          <el-button type="primary" icon-class="skill" size="mini" @click="saveElement"><svg-icon icon-class="save" /></el-button>
        </el-tooltip>
      </el-button-group>
    </div>
    <el-card shadow="hover">
      <el-alert style="margin-bottom: 10px" title="WebSocket 接口定义" type="info" close-text="Get!">
        <div>1、接口名称：给每个接口起一个合理的名字更容易记；</div>
        <div>2、接口方法：分为 send、receive。send 是主动发送消息给服务器。receive 是被动接收服务器发来的消息。</div>
        <div>3、URL：接口的 URL 地址</div>
        <div>4、间隔发送(ms)：设置 间隔发送后，会重复发送该内容</div>
        <div>5、判断报文接收完成：会根据选择来判断一个消息是否接收完成</div>
        <div>6、丢弃：勾选此选项后会正常的发送或接收数据，但不会上报任何消息</div>
        <div>7、URL Params：一般 WebSocket 会在 url 中携带 token 或其它参数，此时可以在这里设置</div>
        <div>8、断言：当发送消息后需要等待服务器返回消息，并判断返回消息是否合法时在这里设置</div>
        <div>9、请求参数：请求参数需要分别输入 URL Params，Body，断言。其中:</div>
        <ul>
          <li>参数名【必填】：参数名称（key）</li>
          <li>参数描述：简述该参数的作用或取值范围，方便理解</li>
          <li>示例值【必填】：参数值（value）的示例</li>
          <li>参数类型：参数类型，如 String、int、file、json 等</li>
          <li>必填：该字段在请求中是否为必填项</li>
        </ul>
      </el-alert>
      <el-form ref="element" size="small" class="demo-table-expand" :rules="rules" label-width="8.4%" label-position="left" :model="element">
        <el-form-item prop="name" label="接口名称">
          <el-input v-model="element.name" placeholder="请输入名称" :class="activeName" />
        </el-form-item>
        <el-row>
          <el-col :span="2">
            <el-form-item prop="method" label="" label-width="0px">
              <el-select v-model="element.method" placeholder="请选择">
                <el-option
                  v-for="item in methodOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="" label-width="0px" prop="host">
              <el-input
                v-model="element.host"
                placeholder="请输入 HOST"
              />
            </el-form-item>
          </el-col>
          <el-col :span="16">
            <el-form-item label="" label-width="0px" prop="uri">
              <el-input
                v-model="element.uri"
                placeholder="请输入 URI"
              />
            </el-form-item>
          </el-col>
          <el-col :span="2">
            <el-form-item prop="method" label-width="0px">
              <template>
                <el-select v-model="element.extra1.handleClass" placeholder="请选择">
                  <el-option
                    v-for="item in afterOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </template>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="element.method === 'receive'">
          <el-col :span="10" :offset="2">
            <el-form-item prop="extra1" label="判断报文接收完成" label-width="130px">
              <template>
                <el-select v-model="element.extra1.endingType" placeholder="请选择" style="width:100%">
                  <el-option
                    v-for="item in endingTypeOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                    :disabled="item.disabled"
                  />
                </el-select>
              </template>
            </el-form-item>
          </el-col>
          <el-col :span="2" :offset="1">
            <el-form-item prop="method" label="" label-width="0px">
              <el-checkbox v-model="element.extra1.lose">丢弃</el-checkbox>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="element.method === 'send'">
          <el-col :span="6" :offset="2">
            <el-form-item prop="method" label="间隔发送(ms)" label-width="100px">
              <el-input-number v-model="element.extra1.sendInterval" :min="0" :step="10000" label="间隔(ms)" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="10" :offset="2">
            <el-form-item prop="extra1" label="判断报文接收完成" label-width="130px">
              <template>
                <el-select v-model="element.extra1.endingType" placeholder="请选择" style="width:100%">
                  <el-option
                    v-for="item in endingTypeOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                    :disabled="item.disabled"
                  />
                </el-select>
              </template>
            </el-form-item>
          </el-col>
          <el-col :span="2" :offset="1">
            <el-form-item prop="method" label="" label-width="0px">
              <el-checkbox v-model="element.extra1.lose">丢弃</el-checkbox>
            </el-form-item>
          </el-col>
        </el-row>
        <el-card shadow="box-card">
          <div style="margin-top: -10px; margin-bottom: -5px; margin-right: 85px; text-align: right;">
            <el-button-group style="z-index:1000; position:absolute;">
              <el-button size="mini" type="primary" @click="handleImportJson">导入 Json</el-button>
            </el-button-group>
          </div>
          <el-tabs v-model="kindActiveName" @tab-click="handleTabClick">
            <el-tab-pane label="URL Params" name="Params" />
            <el-tab-pane label="Body" name="Body" />
            <el-tab-pane label="响应" name="Assert" />
          </el-tabs>
          <!-- URL Params -->
          <div v-if="kindActiveName === 'Params'">
            <div v-if="!value.rawMode" style="height: auto; min-height: 300px; border:solid 1px #dfe4ed;">
              <s-json-schema-editor
                :key="paramsSchemaKey"
                :is-mock="false"
                :schema.sync="paramsSchema"
                :show-edit="false"
                :show-setting="false"
                :show-default-value="true"
                style="margin-top: 10px"
              />
            </div>
            <div v-else class="json-editor" style="border:solid 1px #dfe4ed;">
              <json-editor :key="paramsSchemaKey" ref="paramsJsonEditor" v-model="paramsSchema" />
            </div>
          </div>

          <!-- body -->
          <div v-if="kindActiveName === 'Body'">
            <div v-if="!value.rawMode" style="height: auto; min-height: 300px; border:solid 1px #dfe4ed;">
              <s-json-schema-editor
                :key="bodySchemaKey"
                :is-mock="false"
                :schema.sync="bodySchema"
                :show-edit="false"
                :show-setting="false"
                :show-default-value="true"
                style="margin-top: 10px"
              />
            </div>
            <div v-else class="json-editor" style="border:solid 1px #dfe4ed;">
              <json-editor :key="bodySchemaKey" ref="bodyJsonEditor" v-model="bodySchema" />
            </div>
          </div>

          <!-- assert -->
          <div v-if="kindActiveName === 'Assert'">
            <div v-if="!value.rawMode" style="height: auto; min-height: 300px; border:solid 1px #dfe4ed;">
              <s-json-schema-editor
                :key="assertSchemaKey"
                :is-mock="false"
                :schema.sync="assertSchema"
                :show-edit="false"
                :show-setting="false"
                :show-default-value="true"
                style="margin-top: 10px"
              />
            </div>
            <div v-else class="json-editor" style="border:solid 1px #dfe4ed;">
              <json-editor :key="assertSchemaKey" ref="assertJsonEditor" v-model="assertSchema" />
            </div>
          </div>
        </el-card>
      </el-form>
    </el-card>

    <el-dialog :visible.sync="dialogFormVisible" title="导入 Json" width="800px" :close-on-click-modal="false">
      <el-form ref="dataForm" :rules="rules" label-position="right" label-width="100px">
        <div class="json-editor" style="border:solid 1px #dfe4ed;">
          <json-editor :key="importJsonStrKey" ref="importJsonEditor" v-model="importJsonStr" />
        </div>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="importJson">导 入</el-button>
      </div>
    </el-dialog>

    <el-dialog :visible.sync="historyDialogFormVisible" title="历史记录" width="1200px" :close-on-click-modal="false">
      <api-history-component
        ref="apiHistoryComponent"
        @dblclickResultRow="dblclickResultRow"
      />
    </el-dialog>
  </div>
</template>
<style lang="scss" scoped>
.json-editor {
  text-align-last: left;
  // width: 100%;
  // height: 100%;
  position: relative;

  ::v-deep {
    .CodeMirror {
      height: auto;
      min-height: 300px;
    }

    .CodeMirror-scroll {
      min-height: 300px;
    }

    .cm-s-rubyblue span.cm-string {
      color: #F08047;
    }
  }
}
</style>

<script>
import 'splitpanes/dist/splitpanes.css'
import { create, selectById as apiSelectById, update } from '@/api/functional_test/test_data_manage/api'
import GenerateSchema from 'generate-schema'
import JsonEditor from '@/components/JsonEditor'
import ApiHistoryComponent from '@/components/functional_test/History/apiHistory'

export default {
  components: { JsonEditor, ApiHistoryComponent },
  props: {
    value: {
      type: Object,
      default() {
        return {
          rawMode: false // raw模式
        }
      }
    }
  },
  data() {
    return {
      kindActiveName: 'Params',
      eType: 12, // 元素类型
      activeName: null, // 当前激活的 tab 名称
      eTitle: 'HTTP 接口',
      catalog: {}, // catalog 对象
      element: { // element 对象
        extra1: {}
      },
      dialogFormVisible: false,
      paramsSchema: undefined, // params json schema
      paramsSchemaKey: undefined, // params json schema key (用于刷新json editor、s-json-schema-editor)
      bodySchema: undefined, // body json schema
      bodySchemaKey: undefined, // body json schema key (用于刷新json editor、s-json-schema-editor)
      assertSchema: undefined, // assert json schema
      assertSchemaKey: undefined, // assert json schema key (用于刷新json editor、s-json-schema-editor)
      importJsonStr: {}, // 导入的json
      importJsonStrKey: Date.now(), // 导入的json key
      historyDialogFormVisible: false, // 历史记录弹窗
      rules: {
        name: [
          { required: true, message: 'WebSocket 接口名称不能为空', trigger: 'blur' },
          { max: 255, message: 'WebSocket 接口名称长度不能超过 255 个字符', trigger: 'blur' }
        ],
        uri: [
          { required: true, message: 'URI 不能为空', trigger: 'blur' },
          { max: 255, message: 'URI 长度不能超过 255 个字符', trigger: 'blur' }
        ],
        host: [
          { required: true, message: 'HOST 不能为空', trigger: 'blur' },
          { max: 100, message: 'HOST 长度不能超过 100 个字符', trigger: 'blur' }
        ]
      },
      methodOptions: [
        {
          value: 'send',
          label: 'send'
        }, {
          value: 'receive',
          label: 'receive'
        }],
      afterOptions: [{
        value: 'com.bjy.qa.agent.transport.websocket.WebSocketService',
        label: '默认'
      }, {
        value: 'com.bjy.qa.agent.transport.websocket.BRTCWebSocketService',
        label: 'BRTC'
      }],
      afterDefineValue: '常规',
      endingTypeOptions: [
        {
          value: 1,
          label: '收到数据即认为完成（不处理分包问题）'
        }, {
          value: 2,
          label: '包尾含特殊字符（文本报文）',
          disabled: true
        }, {
          value: 3,
          label: '[包头+包体]格式报文（包头存包体字节长度）',
          disabled: true
        }, {
          value: 4,
          label: '定长报文',
          disabled: true
        }]
    }
  },
  watch: {
  },
  created() {
  },
  methods: {
    // 装载数据
    loadData(id) {
      apiSelectById(this.eType, id).then(response => {
        this.activeName = 'element' // 设置当前 tab element
        this.setElement(response.data)
      })
    },
    // 点击新增用例按钮时调用此方法
    add(parent_id) {
      this.currentCatalogId = parent_id
      this.resetElement()
      this.activeName = 'element' // 设置当前 tab element
    },
    // 点击复制按钮时调用此方法
    copy(id) {
      // 根据要复制的接口类型和用例 ID 查询用例信息
      apiSelectById(this.eType, id).then(response => {
        response.data.id = undefined
        response.data.name = response.data.name + ' - copy'
        if (response.data.apiParams) {
          response.data.apiParams.forEach(function(item) {
            item.id = undefined
          })
        }
        this.$nextTick(() => {
          this.setElement(response.data)
        })
      })
    },
    // 新增时初始化 element 数据
    resetElement() {
      this.element = {
        'projectId': this.$store.getters.projectId,
        'catalogId': this.currentCatalogId,
        'type': this.eType,
        'name': '',
        'url': '',
        'method': 'send',
        'extra1': {
          'handleClass': 'com.bjy.qa.agent.transport.websocket.BRTCWebSocketService', // 后处理模块选择
          'lose': false, // 是否丢弃
          'sendInterval': 0, // 间隔时间
          'endingType': 1 // 结尾标记
        },
        'apiParams': []
      }
      this.resetElementApiParam()

      this.$nextTick(() => {
        this.$refs['element'].clearValidate()
      })
    },
    // 新增时初始化 element.apiParams 数据
    resetElementApiParam() {
      this.paramsSchema = {
        type: 'object',
        properties: {}
      }
      this.paramsSchemaKey = Date.now() + 3
      this.bodySchema = {
        type: 'object',
        properties: {}
      }
      this.bodySchemaKey = Date.now() + 4
      this.assertSchema = {
        type: 'object',
        properties: {}
      }
      this.assertSchemaKey = Date.now() + 5
    },
    // 设置 element 数据（当服务端返回数据后有些类型不符需要在这个方法中转换）
    setElement(ele) {
      this.element = ele
      this.element.extra1 = JSON.parse(this.element.extra1)

      this.resetElementApiParam()
      for (const apiParam of this.element.apiParams) {
        switch (apiParam.kind) {
          case 2: // params
            this.paramsSchema = JSON.parse(apiParam.des)
            break
          case 3: // body
            this.bodySchema = JSON.parse(apiParam.des)
            break
          case 5: // assert
            this.assertSchema = JSON.parse(apiParam.des)
            break
          default:
            console.log('Invalid apiParam.kind, kind: ' + apiParam.kind)
        }
      }
      this.$nextTick(() => {
        this.paramsSchemaKey = Date.now()
        this.bodySchemaKey = Date.now()
        this.assertSchemaKey = Date.now()

        this.$refs['element'].clearValidate()
      })
    },
    // schema 数据格式化（将改过的 schema 为 String 类型的数据，转成 json 对象）
    schemaDataConvert() {
      if (typeof this.paramsSchema === 'string' || this.paramsSchema instanceof String) {
        this.paramsSchema = JSON.parse(this.paramsSchema)
      }
      if (typeof this.bodySchema === 'string' || this.bodySchema instanceof String) {
        this.bodySchema = JSON.parse(this.bodySchema)
      }
      if (typeof this.assertSchema === 'string' || this.assertSchema instanceof String) {
        this.assertSchema = JSON.parse(this.assertSchema)
      }
      this.$nextTick(() => {
        this.paramsSchemaKey = Date.now()
        this.bodySchemaKey = Date.now()
        this.assertSchemaKey = Date.now()
      })
    },
    // raw checkbox change 事件
    handleRawCheckboxChange(check) {
      this.schemaDataConvert()
    },
    // 设置请求参数（apiParam）
    setApiParam(kind, name, schema) {
      if (typeof schema === 'undefined' || typeof schema.properties === 'undefined' || typeof schema.properties !== 'object' || Object.keys(schema.properties).length === 0) { // schema 为空，不处理
        schema = {
          type: 'object',
          properties: {}
        }
      }
      let isExist = false
      for (const apiParam of this.element.apiParams) {
        if (apiParam.kind === kind) {
          apiParam.des = JSON.stringify(schema)
          isExist = true
          break
        }
      }
      if (!isExist) {
        this.element.apiParams.push({
          apiId: this.element.id,
          name: name,
          des: JSON.stringify(schema),
          paramType: 0,
          isRequired: true,
          kind: kind
        })
      }
    },
    // tab 切换
    handleTabClick(tab, event) {
      this.schemaDataConvert()
    },
    // 点击导入 json
    handleImportJson() {
      this.dialogFormVisible = true
      this.importJsonStr = {}
      this.importJsonStrKey = Date.now()
    },
    // 导入 json
    importJson() {
      try {
        let json // 待导入的 json 对象
        if (typeof this.importJsonStr === 'object') {
          json = this.importJsonStr
        } else if (typeof this.importJsonStr === 'string') {
          json = JSON.parse(this.importJsonStr)
        }

        const t = GenerateSchema.json(json)
        delete t.$schema

        if (this.kindActiveName === 'Params') {
          this.paramsSchema = Object.assign({}, t)
          this.paramsSchemaKey = Date.now()
        } else if (this.kindActiveName === 'Body') {
          this.bodySchema = Object.assign({}, t)
          this.bodySchemaKey = Date.now()
        } else if (this.kindActiveName === 'Assert') {
          this.assertSchema = Object.assign({}, t)
          this.assertSchemaKey = Date.now()
        }
        this.$message({ message: 'Json 导入成功!', type: 'success' })
        this.dialogFormVisible = false
      } catch (e) {
        this.$message.error('Json 导入失败：Json 格式错误！')
      }
    },
    // 保存 element 数据
    saveElement() {
      this.$refs['element'].validate((valid) => {
        if (valid) {
          this.element.type = this.eType
          this.element.extra1 = JSON.stringify(this.element.extra1, null, 0)

          this.setApiParam(2, 'params', this.paramsSchema)
          this.setApiParam(3, 'body', this.bodySchema)
          this.setApiParam(5, 'assert', this.assertSchema)

          if (!this.element.id || this.element.id === 0) { // id = 0，新增
            // 跟目录设置 parentId = 0
            if (!this.element.catalogId) {
              this.element.catalogId = 0
            }
            const requestData = Object.assign({}, this.element)
            create(requestData).then((response) => {
              this.element = response.data
              this.element.extra1 = JSON.parse(this.element.extra1)
              this.$emit('create', response.data)
              this.$message({ message: '添加成功!', type: 'success' })
            })
          } else { // 否则修改
            const requestData = Object.assign({}, this.element)
            update(requestData).then((response) => {
              this.element = response.data
              this.element.extra1 = JSON.parse(this.element.extra1)
              this.$emit('update', response.data)
              this.$message({ message: '修改成功!', type: 'success' })
            })
          }
          this.element.extra1 = JSON.parse(this.element.extra1) // 由于异步请求，所以这里要改回成 json 对象
        }
      })
    },
    // 点击 history 按钮
    handleHistory() {
      this.historyDialogFormVisible = true
      this.$nextTick(() => {
        this.$refs.apiHistoryComponent.loadData(this.element.id)
      })
    },
    // 双击历史结果行
    dblclickResultRow(ele) {
      this.historyDialogFormVisible = false
      this.setElement(ele)
    }
  }
}
</script>
